home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / chrome / content / main.js < prev    next >
Encoding:
JavaScript  |  2007-11-12  |  15.3 KB  |  488 lines

  1. /*
  2. # Miro - an RSS based video player application
  3. # Copyright (C) 2005-2007 Participatory Culture Foundation
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  18. */
  19.  
  20. /*****************************************************************************
  21.  Watching for application exit
  22.  *****************************************************************************/
  23.  
  24. var pybridge = Components.classes["@participatoryculture.org/dtv/pybridge;1"].
  25.                 getService(Components.interfaces.pcfIDTVPyBridge);
  26. var jsbridge = Components.classes["@participatoryculture.org/dtv/jsbridge;1"].
  27.                 getService(Components.interfaces.pcfIDTVJSBridge);
  28. var vlcrenderer = Components.classes["@participatoryculture.org/dtv/vlc-renderer;1"].
  29.                 getService(Components.interfaces.pcfIDTVVLCRenderer);
  30.  
  31. var minimizer = Components.classes["@participatoryculture.org/dtv/minimize;1"].
  32.                 getService(Components.interfaces.pcfIDTVMinimize);
  33.  
  34. window.maximized = false;
  35.  
  36. function quitObserver()
  37. {
  38.   this.register();
  39. }
  40.  
  41. quitObserver.prototype = {
  42.   observe: function(subject, topic, data) {
  43.     pybridge.onShutdown();
  44.   },
  45.   register: function() {
  46.     var observerService = Components.classes["@mozilla.org/observer-service;1"]
  47.                           .getService(Components.interfaces.nsIObserverService);
  48.     observerService.addObserver(this, "quit-application", false);
  49.   },
  50.   unregister: function() {
  51.     var observerService = Components.classes["@mozilla.org/observer-service;1"]
  52.                             .getService(Components.interfaces.nsIObserverService);
  53.     observerService.removeObserver(this, "quit-application");
  54.   }
  55. }
  56.  
  57. function getPageCoords (element) {
  58.   var coords = {x : 0, y : 0};
  59.   while (element) {
  60.     coords.x += element.offsetLeft;
  61.     coords.y += element.offsetTop;
  62.     element = element.offsetParent;
  63.   }
  64.   return coords;
  65. }
  66.  
  67. function isDecendentOf(element, id) {
  68.     while (element) {
  69.     if (element.nodeType == 1 && element.id == id) return true
  70.         element = element.parentNode;
  71.     }
  72.     return false;
  73. }
  74.  
  75. /*****************************************************************************
  76.  Volume Knob 
  77.  *****************************************************************************/
  78.  
  79. var VOLUME_SLIDER_LEFT = 25;
  80. var VOLUME_SLIDER_RIGHT = 98;
  81. var VOLUME_SLIDER_WIDTH = VOLUME_SLIDER_RIGHT - VOLUME_SLIDER_LEFT;
  82. var VOLUME_KNOB_OFFSET = 6;
  83.  
  84.  
  85. function translateToVolumeX(event) 
  86. {
  87.   var bottomVolume = document.getElementById("volume");
  88.   var slider = document.getElementById("knob");
  89.   var x = event.screenX - bottomVolume.boxObject.screenX;
  90.   x = x - VOLUME_KNOB_OFFSET;
  91.   return Math.max(VOLUME_SLIDER_LEFT, Math.min(VOLUME_SLIDER_RIGHT, x));
  92. }
  93.  
  94. function volumeKnobDown(event) {
  95.   var slider = document.getElementById("knob");
  96.   slider.beingDragged = true;
  97.   slider.left = translateToVolumeX(event);
  98. }
  99.  
  100. function doVol() {
  101.   var slider = document.getElementById("knob");
  102.   var x = slider.left;
  103.   pybridge.setVolume((x - VOLUME_SLIDER_LEFT) / VOLUME_SLIDER_WIDTH);
  104. }
  105.  
  106. function volumeKnobOut(event) {
  107.   var slider = document.getElementById("knob");
  108.   if(!isDecendentOf(event.relatedTarget, "volume") && slider.beingDragged) {
  109.     doVol();
  110.     slider.beingDragged = false;
  111.   }
  112. }
  113.  
  114. function volumeKnobMove(event) {
  115.   var slider = document.getElementById("knob");
  116.   if (slider.beingDragged) {
  117.     var x = translateToVolumeX(event);
  118.     slider.left = x;
  119.     doVol();
  120.   }
  121. }
  122.  
  123. function volumeKnobUp(event) {
  124.   var slider = document.getElementById("knob");
  125.   if (slider.beingDragged) {
  126.     doVol();
  127.     slider.beingDragged = false;
  128.   }
  129. }
  130.  
  131. /*****************************************************************************
  132.  Video Progress Slider
  133.  *****************************************************************************/
  134.  
  135. var PROGRESS_SLIDER_LEFT = 61;
  136. var PROGRESS_SLIDER_RIGHT = 204;
  137. var PROGRESS_SLIDER_WIDTH = PROGRESS_SLIDER_RIGHT - PROGRESS_SLIDER_LEFT;
  138. var PROGRESS_KNOB_OFFSET = 2;
  139.  
  140.  
  141. function translateToProgressX(event) 
  142. {
  143.   var bottomProgress = document.getElementById("bottom-progress");
  144.   var x = event.screenX - bottomProgress.boxObject.screenX;
  145.   x = x - PROGRESS_KNOB_OFFSET;
  146.   return Math.max(PROGRESS_SLIDER_LEFT, Math.min(PROGRESS_SLIDER_RIGHT, x));
  147. }
  148.  
  149. var videoWasPlaying = false;
  150.  
  151. function videoProgressDown(event) {
  152.   var slider = document.getElementById("progress-slider");
  153.   slider.beingDragged = true;
  154.   slider.left = translateToProgressX(event);
  155.   videoWasPlaying = vlc.playlist.isPlaying;
  156.   if(videoWasPlaying) vlcrenderer.pauseForDrag();
  157. }
  158.  
  159. function doSeek() {
  160.   var slider = document.getElementById("progress-slider");
  161.   var x = slider.left;
  162.   var fractionDone = (x - PROGRESS_SLIDER_LEFT) / PROGRESS_SLIDER_WIDTH;
  163.   var totalTime = vlc.input.length;
  164.   var seekTime = Math.round(totalTime * fractionDone);
  165.   vlc.input.time = seekTime;
  166.   if(videoWasPlaying) vlcrenderer.play();
  167.   slider.beingDragged = false;
  168. }
  169.  
  170. function videoProgressOut(event) {
  171.   var slider = document.getElementById("progress-slider");
  172.   if(!isDecendentOf(event.relatedTarget, "bottom-progress") &&
  173.                   slider.beingDragged) { 
  174.     doSeek();
  175.   }
  176. }
  177.  
  178. function videoProgressMove(event) {
  179.   var slider = document.getElementById("progress-slider");
  180.   if (slider.beingDragged) {
  181.     var x = translateToProgressX(event);
  182.     slider.left = x;
  183.     var fractionDone = (x - PROGRESS_SLIDER_LEFT) / PROGRESS_SLIDER_WIDTH;
  184.     var totalTime = vlc.input.length;
  185.     var seekTime = Math.round(totalTime * fractionDone);
  186.     jsbridge.setSliderText(seekTime);
  187.   }
  188. }
  189.  
  190. function videoProgressUp(event) {
  191.   var slider = document.getElementById("progress-slider");
  192.   if (slider.beingDragged) {
  193.     doSeek();
  194.   }
  195. }
  196.  
  197. /*****************************************************************************
  198.  Main functions
  199.  *****************************************************************************/
  200.  
  201. var vlc = null;
  202.  
  203. function buttonIsActive(buttonId) {
  204.     var elt = document.getElementById(buttonId);
  205.     return (elt.className.indexOf('inactive') == -1);
  206. }
  207.  
  208. function onLoad() {
  209.     jsdump("onLoad running.");
  210.  
  211.     pybridge.addMenubar(document);
  212.  
  213.     // Start watching for application exit.
  214.     // NEEDS: should this move out of onLoad() and be global?
  215.     var qo = new quitObserver();
  216.  
  217.     // Initialize the minimizer class
  218.     minimizer.registerMainWindowProc(window);
  219.  
  220.     // Bring up Python environment.
  221.     pybridge.onStartup(window, document);
  222.  
  223.     // Get a reference te tho vlc plugin
  224.     var videoBrowser = document.getElementById("mainDisplayVideo");
  225.     vlc = videoBrowser.contentDocument.getElementById("video1");
  226.  
  227.     setupHandlers();
  228.     jsdump("onload done");
  229.     minimizer.updateIcon();
  230. }
  231.  
  232. function setSearchEngine(engine) {
  233.     var searchIcon = document.getElementById("search-icon");
  234.     searchIcon.setAttribute("src",'images/search_icon_' + engine + '.png');
  235. }
  236.  
  237. // SeekButton is used for both the rewind/previous and fast forward/next
  238. // buttons.  If clicked, they skip to the next/previous button.  If held downl
  239. // they do a fast forward/rewind.  direction should be 1 for forward, -1 for
  240. // backward.
  241. function setupSeekButton(direction, buttonId) {
  242.   var didSeek = false;
  243.   var timeoutId = null;
  244.   var element = document.getElementById(buttonId);
  245.   var handleTimeout = function() {
  246.     var seekAmount = direction * 3 - 0.5;
  247.     // we want to seek at 3X speed in our current direction (-1 for back, 1
  248.     // for forward).  We also need to take into account that we've played back
  249.     // 0.5 seconds worth of video before the timeout.
  250.     vlc.input.time = vlc.input.time + seekAmount * 1000;
  251.     didSeek = true;
  252.     timeoutId = setTimeout(handleTimeout, 500);
  253.   }
  254.   element.onmousedown = function() { 
  255.     didSeek = false;
  256.     timeoutId = setTimeout(handleTimeout, 500);
  257.     return false;
  258.   }
  259.   element.onmouseup = function() { 
  260.     if(timeoutId) {
  261.         clearTimeout(timeoutId);
  262.         timeoutId = null;
  263.     }
  264.     if(!didSeek) pybridge.skip(direction);
  265.     return false;
  266.   }
  267.   element.onmouseout = function() { 
  268.     if(timeoutId) {
  269.         clearTimeout(timeoutId);
  270.         timeoutId = null;
  271.     }
  272.     return false;
  273.   }
  274. }
  275.  
  276. function setupHandlers() {
  277.     var knob = document.getElementById("volume");
  278.     knob.onmousemove = volumeKnobMove;
  279.     knob.onmousedown = volumeKnobDown;
  280.     knob.onmouseout = volumeKnobOut;
  281.     knob.onmouseup = volumeKnobUp;
  282.  
  283.     // Set up listeners for the progress slider
  284.     var progress = document.getElementById("bottom-progress");
  285.     progress.onmousedown = videoProgressDown;
  286.     progress.onmouseout = videoProgressOut;
  287.     progress.onmousemove = videoProgressMove;
  288.     progress.onmouseup = videoProgressUp;
  289.  
  290.     document.getElementById("bottom-buttons-play").onclick = function() {
  291.         if(buttonIsActive("bottom-buttons-play")) pybridge.playPause();
  292.     };
  293.     document.getElementById("bottom-buttons-stop").onclick = function() {
  294.         if(buttonIsActive("bottom-buttons-stop")) pybridge.stop();
  295.     };
  296.     document.getElementById("bottom-buttons-fullscreen").onclick = function() { 
  297.         if(buttonIsActive("bottom-buttons-fullscreen")) jsbridge.toggleFullscreen();
  298.     }
  299.     setupSeekButton(-1, "bottom-buttons-previous");
  300.     setupSeekButton(1, "bottom-buttons-next");
  301. }
  302.  
  303. function onClose()
  304. {
  305.     pybridge.handleCloseButton();
  306.     return false;
  307. }
  308.  
  309. function minimizeOrRestore()
  310. {
  311.     minimizer.minimizeOrRestore();
  312.     return false;
  313. }
  314.  
  315. function onUnload() {
  316.     pybridge.printOut("onUnload"); 
  317.     if (vlc.playlist.items.count > 0) { 
  318.         vlc.playlist.stop(); 
  319.     } 
  320.     closeApp();
  321.     minimizer.delTrayIcon();
  322. }
  323.  
  324. function jsdump(str) {
  325.     Components.classes['@mozilla.org/consoleservice;1']
  326.     .getService(Components.interfaces.nsIConsoleService)    
  327.     .logStringMessage(str);
  328. }
  329.  
  330. function maximizeOrRestore() {
  331.   if (window.windowState == window.STATE_MAXIMIZED) { 
  332.       window.restore(); 
  333.   } else { 
  334.       window.maximize(); 
  335.   } 
  336. }
  337.  
  338. function showOptionsDialog() {
  339.   if (minimizer.isMinimized()) {
  340.       var features = 'chrome,centerscreen';
  341.   } else { 
  342.       var features = 'chrome,dependent,centerscreen';
  343.   } 
  344.   window.openDialog('chrome://dtv/content/prefs.xul','prefs', features);
  345. }
  346.  
  347. function closeApp() {
  348.   var startup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
  349.        getService(Components.interfaces.nsIAppStartup);
  350.   startup.quit(startup.eAttemptQuit);
  351. }
  352. /*****************************************************************************
  353.  Context menus
  354.  *****************************************************************************/
  355.  
  356. function searchUpForElementWithAttribute(element, attributeName) {
  357.     while (1) {
  358.     if (element.nodeType == 1 && element.getAttribute(attributeName)) {
  359.             return element;
  360.     }
  361.     if (element.parentNode) {
  362.         element = element.parentNode;
  363.         } else {
  364.             return null;
  365.         }
  366.     }
  367.  
  368.     // Satisfy Mozilla that the function always returns a
  369.     // value. Otherwise, we get an error if strict mode is enabled,
  370.     // ultimately preventing us from getting the state change event
  371.     // indicating that the load succeeded.
  372.     return null;
  373. }
  374.  
  375. function doResize(event) {
  376.  if (window.outerWidth < 800) {
  377.     window.outerWidth=800;
  378.   }
  379.  if (window.outerHeight < 500) {
  380.     window.outerHeight=500;
  381.   }
  382.   return true;  
  383. }
  384.  
  385. function onFullscreenActivate() {
  386.     var fullscreenButton = document.getElementById("bottom-buttons-fullscreen");
  387.     if(fullscreenButton.className.indexOf('-inactive') == -1) {
  388.       jsbridge.toggleFullscreen();
  389.     }
  390. }
  391.  
  392. function getClipboardCommands() {
  393.   // This is a really strange way to get an nsIClipboardCommands object, but
  394.   // it's the only way I could make things work -- Ben
  395.   var req = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  396.   var nav = req.getInterface(Components.interfaces.nsIWebNavigation);
  397.   return nav.QueryInterface(Components.interfaces.nsIClipboardCommands);
  398. }
  399.  
  400. function clipboardCopy() {
  401.   getClipboardCommands().copySelection();
  402. }
  403.  
  404. function clipboardCut() {
  405.   getClipboardCommands().cutSelection();
  406. }
  407.  
  408. function clipboardPaste() {
  409.   getClipboardCommands().paste();
  410. }
  411.  
  412. function openFile() {
  413.     var fp = Components.classes["@mozilla.org/filepicker;1"]
  414.             .createInstance(Components.interfaces.nsIFilePicker);
  415.     var openMenuItem = document.getElementById('menuitem-open');
  416.     fp.init(window, openMenuItem.getAttribute('label'),
  417.         Components.interfaces.nsIFilePicker.modeOpen);
  418.     var res = fp.show();
  419.     if (res == Components.interfaces.nsIFilePicker.returnOK){
  420.         pybridge.openFile(fp.file.path);
  421.     }
  422. }
  423.  
  424. function handleExit() {
  425.     pybridge.quit();
  426. }
  427.  
  428. /* This is where the search on chrome events come to hang out */
  429. function onSearchKeyDown(event) {
  430.   if(event.keyCode == 13) {
  431.     /* hack to get engine from the UI */
  432.     var searchIcon = document.getElementById("search-icon");
  433.     var iconURL = searchIcon.getAttribute("src")
  434.     var name = iconURL.substring(19, iconURL.length - 4);
  435.  
  436.     pybridge.performSearch(name, event.target.value);
  437.   }
  438. }
  439.  
  440. /* key presses in the main window.  This is a hack to workaround the fact that
  441.  * XUL doesn't fire events for a couple keys (Enter, Space)
  442.  */
  443. function runCommand(commandId) {
  444.   var command = document.getElementById(commandId);
  445.   if(command) command.doCommand();
  446. }
  447.  
  448. function runMenuItemCommand(menuitemid) {
  449.   var menuitem = document.getElementById(menuitemid);
  450.   if(menuitem.getAttribute('disabled')) return;
  451.   runCommand(menuitem.getAttribute('command'))
  452. }
  453. function onKeyDown(event) {
  454.   if(event.keyCode == 13 && event.altKey) { 
  455.     // Alt+Enter
  456.     runMenuItemCommand('menuitem-fullscreen');
  457.   } else if(event.keyCode == 32 && event.ctrlKey) {
  458.     // Ctrl+Space
  459.     runMenuItemCommand('menuitem-playpausevideo');
  460.   } else if((event.keyCode == 8 && event.ctrlKey) || event.keyCode == 46) {
  461.     // Ctrl-Backspace or Delete
  462.     runCommand("RemoveCurrentSelection");
  463.   } else if(event.keyCode == 38 || event.keyCode == 40) {
  464.     // Up/Down keys
  465.     pybridge.handleKeyPress(event.keyCode, event.shiftKey, event.ctrlKey);
  466.   } else if(event.keyCode == 37 || event.keyCode == 39) {
  467.     // Right/Left Keys
  468.     var videoBox = document.getElementById('video-box');
  469.     if(videoBox.getAttribute("collapsed") == 'true' || searchBoxFocused) return true;
  470.     pybridge.handleKeyPress(event.keyCode, event.shiftKey, event.ctrlKey);
  471.   } else if(event.keyCode == 27) {
  472.     // Escape
  473.     jsbridge.leaveFullscreen();
  474.   } else {
  475.     return true;
  476.   }
  477.   return false;
  478. }
  479.  
  480. var searchBoxFocused = false;
  481. function onSearchBoxFocus() {
  482.   searchBoxFocused = true;
  483. }
  484.  
  485. function onSearchBoxBlur() {
  486.   searchBoxFocused = false;
  487. }
  488.